#ifndef _WX_BUFFER_H
#define _WX_BUFFER_H

#include "wx/wxchar.h"

#include <stdlib.h>

#define DEFINE_BUFFER(classname, chartype, strdupfunc)                      \
  class classname                                            \
  {                                                                           \
    public:                                                                     \
      classname(const chartype *str = NULL)                                   \
        : m_str(str ? strdupfunc(str) : NULL)                               \
      {                                                                       \
      }                                                                       \
      classname(size_t len)                                                   \
        : m_str((chartype *)malloc((len + 1)*sizeof(chartype)))             \
      {                                                                       \
        m_str[len] = (chartype)0;                                           \
      }                                                                       \
      ~classname() { free(m_str); }                                        \
      \
      chartype *release() const                                               \
      {                                                                       \
        chartype *p = m_str;                                                \
        ((classname *)this)->m_str = NULL;                                  \
        return p;                                                           \
      }                                                                       \
      \
      void reset()                                                            \
      {                                                                       \
        free(m_str);                                                        \
        m_str = NULL;                                                       \
      }                                                                       \
      \
      classname(const classname& src)                                         \
        : m_str(src.release())                                              \
      {                                                                       \
      }                                                                       \
      \
      classname& operator=(const chartype *str)                               \
      {                                                                       \
        free(m_str);                                                        \
        m_str = str ? strdupfunc(str) : NULL;                               \
        return *this;                                                       \
      }                                                                       \
      \
      classname& operator=(const classname& src)                              \
      {                                                                       \
        free(m_str);                                                        \
        m_str = src.release();                                              \
        \
        return *this;                                                       \
      }                                                                       \
      \
      bool extend(size_t len)                                                 \
      {                                                                       \
        chartype *                                                          \
        str = (chartype *)realloc(m_str, (len + 1)*sizeof(chartype));   \
        if ( !str )                                                         \
          return false;                                                   \
        \
        m_str = str;                                                        \
        \
        return true;                                                        \
      }                                                                       \
      \
      chartype *data() { return m_str; }                                      \
      const chartype *data() const { return m_str; }                          \
      operator const chartype *() const { return m_str; }                     \
      chartype operator[](size_t n) const { return m_str[n]; }                \
      \
    private:                                                                    \
      chartype *m_str;                                                        \
  }

#define DEFINE_WRITABLE_BUFFER(classname, baseclass, chartype)              \
  class classname : public baseclass                         \
  {                                                                           \
    public:                                                                    \
      classname(const baseclass& src) : baseclass(src) {}                     \
      classname(const chartype *str = NULL) : baseclass(str) {}               \
      operator chartype*() { return this->data(); }                           \
  }

DEFINE_BUFFER( wxCharBuffer, char, wxStrdupA );
DEFINE_WRITABLE_BUFFER( wxWritableCharBuffer, wxCharBuffer, char );

#if wxUSE_WCHAR_T

DEFINE_BUFFER( wxWCharBuffer, wchar_t, wxStrdupW );
DEFINE_WRITABLE_BUFFER( wxWritableWCharBuffer, wxWCharBuffer, wchar_t );

#endif

#undef DEFINE_BUFFER
#undef DEFINE_WRITABLE_BUFFER

typedef wxWCharBuffer wxWxCharBuffer;

#define wxMB2WXbuf wxWCharBuffer
#define wxWX2MBbuf wxCharBuffer
#define wxWC2WXbuf wxChar*
#define wxWX2WCbuf wxChar*

class wxMemoryBufferData {
  public:
    enum { DefBufSize = 1024 };
    friend class wxMemoryBuffer;
  private:
    wxMemoryBufferData( size_t size = wxMemoryBufferData::DefBufSize )
      : m_data( size ? malloc( size ) : NULL ), m_size( size ), m_len( 0 ), m_ref( 0 ) {
    }
    ~wxMemoryBufferData() { free( m_data ); }
    void ResizeIfNeeded( size_t newSize ) {
      if( newSize > m_size ) {
        void *dataOld = m_data;
        m_data = realloc( m_data, newSize + wxMemoryBufferData::DefBufSize );
        if( !m_data ) {
          free( dataOld );
        }
        m_size = newSize + wxMemoryBufferData::DefBufSize;
      }
    }
    void IncRef() { m_ref += 1; }
    void DecRef() {
      m_ref -= 1;
      if( m_ref == 0 ) {
        delete this;
      }
    }
    void  *m_data;
    size_t m_size;
    size_t m_len;
    size_t m_ref;
    DECLARE_NO_COPY_CLASS( wxMemoryBufferData )
};

class wxMemoryBuffer {
  public:
    wxMemoryBuffer( size_t size = wxMemoryBufferData::DefBufSize ) {
      m_bufdata = new wxMemoryBufferData( size );
      m_bufdata->IncRef();
    }
    ~wxMemoryBuffer() { m_bufdata->DecRef(); }
    wxMemoryBuffer( const wxMemoryBuffer& src )
      : m_bufdata( src.m_bufdata ) {
      m_bufdata->IncRef();
    }
    wxMemoryBuffer& operator=( const wxMemoryBuffer& src ) {
      m_bufdata->DecRef();
      m_bufdata = src.m_bufdata;
      m_bufdata->IncRef();
      return *this;
    }
    void  *GetData() const    { return m_bufdata->m_data; }
    size_t GetBufSize() const { return m_bufdata->m_size; }
    size_t GetDataLen() const { return m_bufdata->m_len; }
    void   SetBufSize( size_t size ) { m_bufdata->ResizeIfNeeded( size ); }
    void   SetDataLen( size_t len ) {
      wxASSERT( len <= m_bufdata->m_size );
      m_bufdata->m_len = len;
    }
    void *GetWriteBuf( size_t sizeNeeded ) {
      m_bufdata->ResizeIfNeeded( sizeNeeded );
      return m_bufdata->m_data;
    }
    void  UngetWriteBuf( size_t sizeUsed ) { SetDataLen( sizeUsed ); }
    void *GetAppendBuf( size_t sizeNeeded ) {
      m_bufdata->ResizeIfNeeded( m_bufdata->m_len + sizeNeeded );
      return ( char* )m_bufdata->m_data + m_bufdata->m_len;
    }
    void  UngetAppendBuf( size_t sizeUsed ) {
      SetDataLen( m_bufdata->m_len + sizeUsed );
    }
    void  AppendByte( char data ) {
      wxCHECK_RET( m_bufdata->m_data, wxT( "invalid wxMemoryBuffer" ) );
      m_bufdata->ResizeIfNeeded( m_bufdata->m_len + 1 );
      *( ( ( char* )m_bufdata->m_data ) + m_bufdata->m_len ) = data;
      m_bufdata->m_len += 1;
    }

    void  AppendData( const void *data, size_t len ) {
      memcpy( GetAppendBuf( len ), data, len );
      UngetAppendBuf( len );
    }

    operator const char *() const { return ( const char* )GetData(); }

  private:
    wxMemoryBufferData*  m_bufdata;
};

#endif
